package entities;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import option.KBVocabulary;

import kb.RBox;
import kb.TBox;

//import preprocessor.absorption.Absorption;
import preprocessor.absorption.Absorption;
import preprocessor.absorption.BinaryAbsorptionGuarded;
import preprocessor.absorption.extended.OptimizedRestrictions;

import tableaux.Completion;
//import tableaux.blocking.OptimizedDoubleBlocking;
import tableaux.rules.RuleAnd;
import tableaux.rules.RuleExists;
import tableaux.rules.RuleForAll;
import tableaux.rules.RuleLazyUnfolding;
import tableaux.rules.RuleNominal;
import tableaux.rules.RuleOr;
import utils.exception.CoreReasoningException;

import entities.concept.Concept;
import entities.concept.AtomicConcept;
import entities.concept.DomainConcept;
import entities.concept.Nominal;
import entities.concept.PConcept;
import entities.concept.UniversalConcept;
import entities.role.Role;

/**
 * All individuals are assumed to be ordered for implementing blocking.
 * 
 * Blocking for Inverse roles (I), cf. a DL with transitive and inverse roles
 * and role hierarchies by Ian and Ulrike. Sec. 2 Dynamic blocking: remember
 * this individual is blocked by whom --> to be broken later Non-generating
 * rule: still applicable to blocked nodes Non-blockable nodes: only for
 * nominals
 * 
 * @author j55wu
 * 
 */

public final class Individual implements Comparable<Individual> {
	private static final String anonIdPrefix = "AnonInd_";
	private static final String intIdPrefix = "TempInd_";
	private static int anonIdCount = 0;
	// private static final String blankIdPrefix = "Blank_Internal_Ind_";
	// private static int blankIdCount = 0;
	private static HashMap<String, Individual> allIndividuals = new HashMap<String, Individual>();
	// //////
	private final String name;
	private int order;
	private Map<Role, HashSet<Individual>> children;
	private Map<Role, HashSet<Individual>> parents;
	private Individual generatingParent;
	// this node may be dependent on by nodes it creates in the exist rule. RAs
	// not considered.
	private Set<Integer> generatedByIndDeps;
	private Set<Concept> labels;
	private Set<Concept> expandedLabels;
	private Set<Concept> unfoldedLabels;
	private Map<AtomicConcept, Set<AtomicConcept>> binaryExpanded;
	private Map<PConcept, Set<PConcept>> genBinaryExpanded; //also ordered by DEFs
	// //////
	private Set<Nominal> noms;
	// //////
	private boolean generalBinaryKeyAddedChange;
	private boolean binaryKeyAddedChange; // for binary axioms only
	private boolean domainKeyAddedChange; // for domain guards rule
	private boolean cnKeyAddedChange;// for Tu axioms
	private boolean cnNegKeyAddedChange;// for Tnu axioms
	private boolean tfDomainCpChange;
	private boolean isSat;
	private boolean blockedStatus;
	
	private boolean root=false;
	
	public void setAsTableauRoot(){
		this.root = true;
	}

	// private boolean appliedGCI = false;
	// /////////////////////////////
	public static void resetIndCount() {
		anonIdCount = 0;
	}

	// a visible ind will be added for reasoning, can be used to filter blank
	// nodes
	public boolean isVisible() {
		// if(!Completion.blankIndiVisible &&
		// this.getName().startsWith(Individual.getBlankIndNamePrefix()) ){
		// return false;
		// }
		return true;
	}

	public Individual getGeneratingParents() {
		return this.generatingParent;
	}

	public Set<Individual> getGeneratingAncestors(boolean excludeNominals) {
		Set<Individual> ret = new HashSet<Individual>();
		if (this.generatingParent != null
				&& (!excludeNominals || this.isBlockable())) {
			ret.add(this.generatingParent);
			ret.addAll(this.generatingParent
					.getGeneratingAncestors(excludeNominals));
		}
		return ret;
	}

	public void resetGeneratingParent() {
		this.generatingParent = null;
	}

	public void setGeneratingParent(Individual p) {
		if (this.generatingParent == null && !this.root) {
			this.generatingParent = p;
		} else {
//			System.err.println("Parent already exists for " + this+"; not added: "+p);
		}
	}

	public Map<Role, HashSet<Individual>> getParentsMap() {
		return this.parents;
	}

	public Map<Role, HashSet<Individual>> getChildrenMap() {
		return this.children;
	}

	public boolean isSat() {
		return this.isSat;
	}

	public void setSat(boolean val) {
		this.isSat = val;
	}

	public Set<Nominal> getNominals() {
		return this.noms;
	}

	public void addGeneratedByDep(Set<Integer> deps) {
		this.generatedByIndDeps.addAll(deps);
	}

	public void addGeneratedByDepMax(Integer dep) {
		this.generatedByIndDeps.add(dep);
	}

	public Set<Integer> getGeneratedBy() {
		return this.generatedByIndDeps;
	}

	public Integer getGeneratedByMax() {
		if (this.generatedByIndDeps.size() == 1) {
			return this.generatedByIndDeps.iterator().next();
		}
		if (this.generatedByIndDeps.size() < 1) {
			return 0;
		}
		List<Integer> deps = new ArrayList<Integer>(this.generatedByIndDeps);
		Collections.sort(deps);
		return deps.get(deps.size() - 1);
	}

	// public Set<Individual> getAllDepByInds(){
	// Set<Individual> ret = new HashSet<Individual>();
	// ret.addAll(depByInds);
	// for(Individual dep:depByInds){
	// ret.addAll(dep.getAllDepByInds());
	// }
	// return ret;
	// }

	private void init() {
		this.order = anonIdCount++;
		Individual.allIndividuals.put(this.name, this);
		this.children = new HashMap<Role, HashSet<Individual>>();
		this.parents = new HashMap<Role, HashSet<Individual>>();
		this.generatingParent = null;
		this.generatedByIndDeps = new HashSet<Integer>();
		this.labels = new HashSet<Concept>();
		this.expandedLabels = new HashSet<Concept>();
		this.unfoldedLabels = new HashSet<Concept>();
		this.binaryExpanded = new HashMap<AtomicConcept, Set<AtomicConcept>>();
		this.noms = new HashSet<Nominal>();
		this.isSat = true;
		this.binaryKeyAddedChange = false;
		this.generalBinaryKeyAddedChange = false;
		this.domainKeyAddedChange = false;
		this.cnKeyAddedChange = false;
		this.cnNegKeyAddedChange = false;
		this.tfDomainCpChange = false;
		this.blockedStatus = false;
	}


	// when to begin another reasoning task
	public static int resetAllIndividuals() {
		Individual.allIndividuals.clear();
		// also need to remove nominal interpretation individuals?
		// System.out.println("Related nominals: "+Nominal.getNominalMaps().keySet());
		// for(Nominal o:Nominal.getAllNominals()){
		int ct = Nominal.getReasonedNominals().size();
		for (Nominal o : Nominal.getReasonedNominals()) {
			o.intepretNominal(null);
		}
		// System.err.println("\nUsed Noms:\n"+Nominal.getReasonedNominals());
		Nominal.clearReasonedNominals();
		// Nominal.clearNominalMap();
		return ct;
		// Nominal.clearNominalMap();
	}

	public void cleanUp() {
		this.children.clear();
		this.parents.clear();
		this.generatingParent = null;
		this.labels.clear();
		this.generatedByIndDeps.clear();
		this.expandedLabels.clear();
		this.unfoldedLabels.clear();
		this.binaryExpanded.clear();
		this.noms.clear();
		this.isSat = true;
		this.binaryKeyAddedChange = false;
		this.generalBinaryKeyAddedChange = false;
		this.domainKeyAddedChange = false;
		this.cnKeyAddedChange = false;
		this.tfDomainCpChange = false;
		this.cnNegKeyAddedChange = false;
		// this.appliedGCI = false;
		this.blockedStatus = false;
	}

	/*
	 * return true if one of the two conditions are met 1. UNA is enabled and a
	 * has a different name from b; or 2. explicit assertion (a :differentFrom
	 * b) is given. return false otherwise.
	 */
	public boolean isDifferentFrom(Individual that) {
		return false;
	}

	// public boolean isSameIndividual(Individual that){
	// return false;
	// }

	// keep track of all different pairs of nodes
	public void getDifferentInds() {
		return;
	}

	public void setDifferentInd(Individual that) {
		return;
	}

	public String getName() {
		return this.name;
	}

	// public static void clearAllIndividuals(){
	// //must clear all individuals for each new reasoning service
	// Individual.allIndividuals.clear();
	// }

	// instance controlled
	public static Individual newIndividual(String name) {
		if (Individual.allIndividuals != null
				&& Individual.allIndividuals.containsKey(name)) {
			return Individual.allIndividuals.get(name);
		}
		return new Individual(name);
	}

	public static Individual getStarIndividual() {
		Individual ind = new Individual(KBVocabulary.SPECIAL_IND_SYMB);
		// //this individual is not supposed to be reasoned with.
		// Individual.allIndividuals.remove(KBVocabulary.SPECIAL_IND_SYMB);
		return ind;
	}

	public static Individual getAnonIndividual() {
		return new Individual(anonIdPrefix + anonIdCount);
	}
	
	public static Individual getInternalIndividual() {
		return new Individual(intIdPrefix + anonIdCount);
	}

	private Individual(String name) {
		this.name = name;
		init();
	}

	public Map<String, Individual> getAllIndividuals() {
		return allIndividuals;
	}

	public int getOrder() {
		return this.order;
	}

	// only direct presence is considered
	private void extractNominalFromLabels() {
		for (Concept c : this.labels) {
			if (c instanceof Nominal) {
				this.noms.add((Nominal) c);
			}
		}
	}

	// a node is nominal or blockable
	public boolean isBlockable() {
		if (noms.size() > 0) {
			return false;
		}
		this.extractNominalFromLabels();
		return this.noms.size() == 0;
	}

	public boolean isNominalNode() {
		return !isBlockable();
	}

	// public boolean blocked (){
	// boolean ret = false;
	// // for(Individual n: this.getAncestors()){
	// // ret |= n.labels.containsAll(this.labels) || !n.isSat;
	// // }
	// // for(Individual n: this.getParents()){
	// // ret |= !n.isSat;
	// // }
	// return ret |= !this.getGeneratingParents().isSat;
	// }

	// public boolean blockedGeneratingRule(){
	// boolean ret = false;
	// for(Individual n: this.getParents()){
	// ret |= n.labels.containsAll(this.labels);
	// }
	// return ret;
	// }

	public Set<Concept> getUnfolededLabels() {
		return this.unfoldedLabels;
	}

	public boolean addUnfoldedLabels(Concept c) {
		return this.unfoldedLabels.add(c);
	}

	public Map<AtomicConcept, Set<AtomicConcept>> getBinaryExpanded() {
		return this.binaryExpanded;
	}

	// by default, we follow the default entry order
	public void addBinaryExpanded(AtomicConcept k1, AtomicConcept k2)
			throws CoreReasoningException {
		Nominal o = null;
		AtomicConcept ac = null;
		if (k1 instanceof Nominal) {
			o = (Nominal) k1;
			ac = k2;
		} else if (k2 instanceof Nominal) {
			o = (Nominal) k2;
			ac = k1;
		} else if (Completion.partiallyGuardedReasoning) {
			throw new CoreReasoningException(
					"Must have one nominal in the keys!");
		}
		if (BinaryAbsorptionGuarded.defOrder
				.equals(BinaryAbsorptionGuarded.entryOrder.Nominal_First)) {
			this.addBinaryEntry(o, ac);
			return;
		}
		if (BinaryAbsorptionGuarded.defOrder
				.equals(BinaryAbsorptionGuarded.entryOrder.Nominal_Second)) {
			this.addBinaryEntry(ac, o);
			return;
		}
	}
	
	public void addGeneralBinaryExpanded(PConcept defr, PConcept ac)
			throws CoreReasoningException {
		Set<PConcept> vs = null;
		if (genBinaryExpanded.containsKey(defr)) {
			vs = genBinaryExpanded.get(defr);
		} else {
			vs = new HashSet<PConcept>();
		}
		vs.add(ac);
		genBinaryExpanded.put(defr, vs);
	}
	
	public void removeGeneralBinaryEntry(PConcept defr, PConcept ac) {
		if (genBinaryExpanded != null && genBinaryExpanded.containsKey(defr)) {
			genBinaryExpanded.get(defr).remove(ac);
		}
	}

	public void removeBinaryEntry(AtomicConcept k1, AtomicConcept k2) {
		if (binaryExpanded != null && binaryExpanded.containsKey(k1)) {
			binaryExpanded.get(k1).remove(k2);
		}
	}
	
	public boolean hasFiredGeneralBinaryEntry(PConcept defr, PConcept ac) {
		return this.genBinaryExpanded.containsKey(defr) && 
				this.genBinaryExpanded.get(defr).contains(ac);
	}

	public boolean hasFiredBinaryEntry(AtomicConcept k1, AtomicConcept k2) {
		Nominal o = null;
		AtomicConcept ac = null;
		if (k1 instanceof Nominal) {
			o = (Nominal) k1;
			ac = k2;
		} else if (k2 instanceof Nominal) {
			o = (Nominal) k2;
			ac = k1;
		} else {
			return false;
		}
		if (BinaryAbsorptionGuarded.defOrder
				.equals(BinaryAbsorptionGuarded.entryOrder.Nominal_First)) {
			return binaryExpanded.containsKey(o)
					&& binaryExpanded.get(o).contains(ac);
		}
		if (BinaryAbsorptionGuarded.defOrder
				.equals(BinaryAbsorptionGuarded.entryOrder.Nominal_Second)) {
			return binaryExpanded.containsKey(ac)
					&& binaryExpanded.get(ac).contains(o);
		}
		return true;
	}

	private void addBinaryEntry(AtomicConcept k1, AtomicConcept k2) {
		Set<AtomicConcept> vs = null;
		if (binaryExpanded.containsKey(k1)) {
			vs = binaryExpanded.get(k1);
		} else {
			vs = new HashSet<AtomicConcept>();
		}
		vs.add(k2);
		binaryExpanded.put(k1, vs);
	}

	public void removeLabels(Concept c) {
		this.labels.remove(c);
	}

	public void removeLabels(HashSet<Concept> cs) {
		this.labels.removeAll(cs);
	}

	// IMPORTANT: the *only* access method to add something to the label
	// return true if a concept is new to the labels
	public boolean addLabels(Concept c) {
		if (!this.labels.contains(c)) {
			// System.out.println("adding: "+this+" : " + c);
			if (c instanceof AtomicConcept) {
				if (c instanceof PConcept) {
					PConcept pc = (PConcept) c;
					this.cnKeyAddedChange = true;
					if (pc.isGuard()) {
						this.binaryKeyAddedChange = true;
						if(OptimizedRestrictions.OptimizeEx && pc.isRoleGuard()){
							this.generalBinaryKeyAddedChange = true;
						}
					}else if(OptimizedRestrictions.OptimizeEx){
						this.generalBinaryKeyAddedChange = true;
					}
//					else {
//						this.cnKeyAddedChange = true;
//					}
				} else {
					this.binaryKeyAddedChange = true;
					if (Completion.nominalInTu && c instanceof Nominal) {
						this.cnKeyAddedChange = true;
					}
				}

			} else if (c instanceof DomainConcept) {
				this.domainKeyAddedChange = true;
				this.tfDomainCpChange = true;
			} else if (Absorption.NCA && c.isSimpleNot()) {
				this.cnNegKeyAddedChange = true;
			}
			// if(c instanceof Nominal){
			// Nominal o = (Nominal) c;
			// o.addAttachedIndividual(this);
			// }
			this.labels.add(c);
			return true;
		}
		return false;
	}

	public boolean getDomainKeyAddedChange() {
		return this.domainKeyAddedChange;
	}
	
	public boolean getTfKeyChange(){
		return this.tfDomainCpChange;
	}
	
	public void unsetTfKeyChange(){
		tfDomainCpChange = false;
	}

	// back to false
	public void unsetDomainKeyAddedChange() {
		this.domainKeyAddedChange = false;
	}

	public boolean getGeneralBinaryKeyAddedChange() {
		return this.generalBinaryKeyAddedChange;
	}

	public void unsetGeneralBinaryKeyAddedChanges() {
		this.generalBinaryKeyAddedChange = false;
	}
	
	
	public boolean getBinaryKeyAddedChange() {
		return this.binaryKeyAddedChange;
	}

	public void unsetBinaryKeyAddedChanges() {
		this.binaryKeyAddedChange = false;
	}

	public boolean getCnKeyAddedChange() {
		return this.cnKeyAddedChange;
	}

	public boolean getCnNegKeyAddedChange() {
		return this.cnNegKeyAddedChange;
	}

	public void unsetCnKeyAddedChange() {
		this.cnKeyAddedChange = false;
	}

	public void unsetCnNegKeyAddedChange() {
		this.cnNegKeyAddedChange = false;
	}

	// private void addLabels(Set<Concept> cs){
	// for(Concept c:cs){
	// this.addLabels(c);
	// }
	//
	// }

	public Set<Concept> getLabels() {
		// if(Completion.fullyGuardedReasoning){
		// return Completion.removeIrrelevantFeatures(labels);
		// }else{
		// return this.labels;
		// }
		return this.labels;
	}

	// for testing purpose ONLY
	public List<Concept> getSortedLabels() {
		List<Concept> all = new ArrayList<Concept>();
		all.addAll(this.labels);
		Collections.sort(all, new Comparator<Concept>() {

			public int compare(Concept c1, Concept c2) {
				return c1.toSerializedString().compareTo(
						c2.toSerializedString());
			}

		});
		return all;
	}

	public Set<Concept> getExpandedLabels() {
		return this.expandedLabels;
	}

	public boolean addExpandedLabels(Concept c) {
		return this.expandedLabels.add(c);
	}

	public boolean addExpandedLabels(HashSet<Concept> c) {
		return this.expandedLabels.addAll(c);
	}

	// //////////////////////////////////////////////////////
	/*
	 * If argument is true, then cycles will add oneself to its own ancestors.
	 */
	public static Set<Individual> ancChecked = new HashSet<Individual>();

	public Set<Individual> getAllAncestors(Set<Individual> ancs,
			boolean excludeNominals) {
		if (!excludeNominals || this.isBlockable()) {
			ancChecked.add(this);
		}
		Set<Individual> pas = this.getParents();
		// if(pas.size() > 100){
		// System.out.println("parents for "+this+": "+pas.size());
		// }
		for (Individual n : pas) {
			if (ancs.contains(n) || (excludeNominals && n.isNominalNode())) {
				continue;
			} else {
				ancs.add(n);
				if (!ancChecked.contains(n)) {
					n.getAllAncestors(ancs, excludeNominals);
				}
			}
		}
		// if(!selfAllowed){
		// ancs.remove(this);
		// }
		return ancs;
	}

	private static Set<Individual> checked = new HashSet<Individual>();

	public boolean hasThisAncestor(Individual anc) {
		boolean ret = false;
		for (Individual n : this.getParents()) {
			if (n.equals(anc)) {
				ret |= true;
				break;
			} else {
				if (checked.contains(n)) {
					continue;
				} else {
					checked.add(n);
					ret |= n.hasThisAncestor(anc);
				}
				if (ret) {
					break;
				}
			}
		}
		if (ret) {
			checked.clear();
		}
		return ret;
	}

	// private void addAncestors(Role r, HashSet<Individual> ns){
	// if(this.ancestors.containsKey(r)){
	// this.ancestors.get(r).addAll(ns);
	// }else{
	// this.ancestors.put(r, ns);
	// }
	// }

	// private void removeAncestors(Role r, HashSet<Individual> ns){
	// if(this.ancestors.containsKey(r)){
	// this.ancestors.get(r).removeAll(ns);
	// if(this.ancestors.get(r).size()<1){
	// this.ancestors.remove(r);
	// }
	// }
	// }
	
	//find all parents using role inclusions
	public Set<Individual> getParents(Role r, TBox tb) {
		if (this.parents == null) {
			return null;
		}
		Set<Individual> ret = new HashSet<Individual>();
		if(!tb.getRoleBox().getSubRolesMap().containsKey(r)){
			Set<Individual> chi = this.parents.get(r);
			if(chi!=null){
				ret.addAll(chi);
			}
			return ret;
		}
		//get all subroles
		for(Role sub:tb.getRoleBox().getAllSubRoles(r)){
			if (this.parents.containsKey(sub)) {
				ret .addAll(this.parents.get(sub) );
			}
			
		}
		return ret;
	}

//	public Set<Individual> getParents(Role r) {
//		if (this.parents == null) {
//			return null;
//		}
//		if (this.parents.containsKey(r)) {
//			return this.parents.get(r);
//		}
//		return null;
//	}

	/*
	 * Get all parents with all roles
	 */
	public Set<Individual> getParents() {
		if (this.parents == null) {
			return null;
		}
		HashSet<Individual> ret = new HashSet<Individual>();
		for (Role r : this.parents.keySet()) {
			ret.addAll(this.parents.get(r));
		}
		return ret;
	}

	// ///////////////////////
//	public Set<Role> relatedRoleForParent(Individual that) {
//		Set<Role> rs = new HashSet<Role>();
//		for (Role r : this.parents.keySet()) {
//			if (this.parents.get(r).contains(that)) {
//				rs.add(r);
//			}
//		}
//		return rs;
//	}

	public Set<Role> relatedRoleForChild(Individual that) {
		Set<Role> rs = new HashSet<Role>();
		for (Role r : this.children.keySet()) {
			if (this.children.get(r).contains(that)) {
				rs.add(r);
			}
		}
		return rs;
	}

//	public boolean hasParent(Role r, Individual an) {
//		if (this.parents != null && this.parents.containsKey(r)) {
//			return this.parents.get(r).contains(an);
//		}
//		return false;
//	}

//	public boolean hasChild(Role r, Individual sn) {
//		if (this.children != null && this.children.containsKey(r)) {
//			return this.children.get(r).contains(sn);
//		}
//		return false;
//	}

	// maintain parent-children info below: manually add m-R->n pairs for ALCI
	public boolean addParents(Role r, Individual n) {
		HashSet<Individual> nss;
		if (!this.parents.containsKey(r)) {
			nss = new HashSet<Individual>();
			nss.add(n);
			this.parents.put(r, nss);
			return true;
		} else {
			return this.parents.get(r).add(n);
		}
	}

	public void addParents(Role r, Set<Individual> ns) {
		for (Individual ob : ns) {
			this.addParents(r, ob);
		}
	}

	public boolean removeParents(Role r, Individual n) {
		if (this.parents.containsKey(r)) {
			HashSet<Individual> ancs = this.parents.get(r);
			if (ancs.contains(n)) {
				ancs.remove(n);
				if (ancs.size() < 1) {
					this.parents.remove(r);
				}
				return true;
			}
		}
		return false;
		// remove this from ns's successors
		// if(ns.hasSuccessor(r, this)){
		// ns.removeSucessors(r, this);
		// }
	}

	public void removeParents(Role r, HashSet<Individual> obs) {
		for (Individual ob : obs) {
			this.removeParents(r, ob);
		}
	}

	public boolean addChild(Role r, Individual n) {
		HashSet<Individual> nss;
		if (!this.children.containsKey(r)) {
			nss = new HashSet<Individual>();
		} else {
			nss = this.children.get(r);
		}
		boolean ret = nss.add(n);
		this.children.put(r, nss);
		return ret;
	}

	public void addChild(Role r, HashSet<Individual> ns) {
		for (Individual n : ns) {
			this.addChild(r, n);
		}
	}

	public boolean removeChild(Role r, Individual n) {
		if (this.children.containsKey(r)) {
			HashSet<Individual> ancs = this.children.get(r);
			if (ancs.contains(n)) {
				ancs.remove(n);
				if (ancs.size() < 1) {
					this.children.remove(r);
				}
				return true;
			}
		}
		return false;
	}

	public void removeChild(Role r, HashSet<Individual> ns) {
		for (Individual n : ns) {
			this.removeChild(r, n);
		}
	}

	// ////////////////////
	//find all children using role inclusions
	public Set<Individual> getChildren(Role r, TBox tb) {
		if (this.children == null) {
			return null;
		}
		Set<Individual> ret = new HashSet<Individual>();
		if(!tb.getRoleBox().getSubRolesMap().containsKey(r)){
			Set<Individual> chi = this.children.get(r);
			if(chi!=null){
				ret .addAll(chi);
			}
			return ret;
		}
		//get all subroles
		for(Role sub:tb.getRoleBox().getAllSubRoles(r)){
			if (this.children.containsKey(sub)){
				ret .addAll(this.children.get(sub) );
			}
			
		}
		return ret;
	}
	
	public Set<Individual> getChildren(Role r) {
		if (this.children == null) {
			return null;
		}
		if (this.children.containsKey(r)) {
			return this.children.get(r);
		}
		return null;
	}

	/*
	 * Get all successors with all roles
	 */
	public Set<Individual> getChildren() {
		if (this.children == null) {
			return null;
		}
		Set<Individual> ret = new HashSet<Individual>();
		for (Role r : this.children.keySet()) {
			if (this.children.containsKey(r)) {
				ret.addAll(this.children.get(r));
			}
		}
		return ret;
	}

	// restore a certain type of concepts to unexpanded labels
	//now need to consider role hierarchy
	public boolean restoreForallConceptToExpandedLabels(Role r, RBox rb) {
		boolean done = false;
		for (Concept c : this.getLabels()) {
			if (c instanceof UniversalConcept) {
				// test if that role is this role
				UniversalConcept rc = (UniversalConcept) c;
				Role ar = rc.getRole();
				Set<Role> roles = rb.getAllSuperRoles(r);
				if(roles == null){
					roles = new HashSet<Role>();
					roles.add(r);
				}
//				if (rc.getRole().equals(r)) {
				if (roles.contains(ar)) {
					done |= true;
					if (this.getExpandedLabels().contains(rc)) {
						this.getExpandedLabels().remove(rc);
					}
				}
			}
		}
		return done;
	}

	// //////////////////////////////////////////////////////////////////

	@Override
	public String toString() {
		return this.name;
	}

	public Individual getPrimeCopy() {
		return new Individual(this.name + entities.concept.Concept.PRIME);
	}

	public boolean isCompleted() {
		return this.expandedLabels.containsAll(this.labels);
	}

	public boolean moreRulesApplicable(Completion dc) {
		RuleNominal or = null;
		if (Completion.MergeNominal) {
			or = new RuleNominal(dc);
		}
		RuleAnd cr = new RuleAnd(dc);
		RuleOr dr = new RuleOr(dc);
		RuleExists er = new RuleExists(dc);
		RuleForAll fr = new RuleForAll(dc);
		RuleLazyUnfolding ar = new RuleLazyUnfolding(dc);
		if ((or != null && or.isApplicable(this)) || cr.isApplicable(this)
				|| dr.isApplicable(this) || er.isApplicable(this)
				|| fr.isApplicable(this) || ar.isApplicable(this)) {
			return true;
		}
		return false;
	}

	// add all binary fired rules to expanded set
	public void addBinaryToExpandedLabels() {
		if (this.getBinaryExpanded().size() < 1) {
			return;
		} else {
			for (Concept cc : this.getBinaryExpanded().keySet()) {
				expandedLabels.addAll(this.getBinaryExpanded().get(cc));
				expandedLabels.add(cc);
			}
		}

	}

	public int compareTo(Individual o) {
//		return this.order - o.order;
		return this.name.compareTo(o.name);
	}

	// public void setAppliedGCI(boolean appliedGCI) {
	// this.appliedGCI = appliedGCI;
	// }

	public boolean isBlocked() {
		return this.blockedStatus;
	}

	public void setBlockStatus(boolean tf) {
		this.blockedStatus = tf;
	}

	// public boolean hasAppliedGCI() {
	// return appliedGCI;
	// }

	// private static long safecount =0;
	// private static long tsize =0;
	/* 
	 * A R-neighbor of x: its R-succesor or its R-inverse predecessor (use role inclusions!).
	 * true if this node has no neighbors at all. Or: In case that for all its
	 * neighbors such that this->R->n, or n->IR->this, and n has C in its label.
	 * n is safe if *this* is blockable or this is nominal but n is not blocked.
	 */
	public boolean hasSafeNeighbor(Role r, Concept C, TBox tb) {
		// safecount++;
		boolean block = this.isBlockable();
		Set<Individual> rs = this.getChildren(r, tb);
		if (rs != null && rs.size()>0) {
			if(C.isTop()){
				return true;
			}
			for (Individual suc : rs) {
				boolean safe = sucHasC(block, suc, C);
				if (safe) {
					// tsize += rs.size();
					return true;
				}
			}
		}
		Role ir = tb.getRoleBox().getInverseRole(r);
		Set<Individual> irs = this.getParents(ir, tb);
		if (irs != null) {
			for (Individual suc : irs) {
				boolean safe = sucHasC(block, suc, C);
				if (safe) {
					// tsize += rs.size();
					return true;
				}
			}
		}
		// if(rs == null && irs == null){
		// return false;
		// }
		// Set<Individual> nbs = new HashSet<Individual>(2*rs.size());
		// if(rs != null){
		// nbs.addAll(rs);
		// }
		// if(irs != null){
		// nbs.addAll(irs);
		// }

		// for every neighbor n, if n contains C, and
		// for(Individual n:nbs){
		// changed from n.getLabels().contains(C) ??? why??
		// changed back from
		// n.getLabels().containsAll(Concept.reduceToConjuncts(C))
		// if(n.getLabels().containsAll(Concept.reduceToConjuncts(C))){

		// }
		// if(safecount % 10000 == 0)
		// System.err.printf("Safe neighbors called:%d, avg size so far:%8.1f\n\n",safecount,(tsize*1.0/safecount));
		return false;
	}

	private boolean sucHasC(boolean blockablex, Individual n, Concept C) {
		if (n.getLabels().contains(C)) {
			if (blockablex || !n.isBlocked()) {
				return true;
			}
		}
		return false;
	}

	private volatile int hashCode = 0;

	@Override
	public int hashCode() {
		if (hashCode != 0) {
			return hashCode;
		}
		final int prime = 31;
		int result = 17;
		result = prime * result
				+ ((this.name == null) ? 0 : this.name.hashCode());
		hashCode = result;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Individual)) {
			return false;
		}
		Individual other = (Individual) obj;
		if (this.name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!this.name.equals(other.name)) {
			return false;
		}
		return true;
	}

}
